package cn.ante.ymcc.service.impl;

import cn.ante.ymcc.constance.CommonExceptionEnum;
import cn.ante.ymcc.constance.RedisConstant;
import cn.ante.ymcc.domain.*;
import cn.ante.ymcc.dto.RedisSMSCodeValueDto;
import cn.ante.ymcc.dto.UserRegisterDTO;
import cn.ante.ymcc.exception.GlobalException;
import cn.ante.ymcc.feign.UaaFeignClient;
import cn.ante.ymcc.mapper.UserAccountMapper;
import cn.ante.ymcc.mapper.UserBaseInfoMapper;
import cn.ante.ymcc.mapper.UserRedCodeMapper;
import cn.ante.ymcc.result.JSONResult;
import cn.ante.ymcc.util.AssertUtil;
import cn.ante.ymcc.util.BitStatesUtils;
import cn.ante.ymcc.mapper.UserMapper;
import cn.ante.ymcc.service.IUserService;
import cn.ante.ymcc.util.StrUtils;
import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author Nai
 * @since 2022-08-05
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;


    @Autowired
    private UserBaseInfoMapper userBaseInfoMapper;

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private UaaFeignClient uaaFeignClient;

    @Autowired
    private UserRedCodeMapper userRedCodeMapper;

    private PasswordEncoder passwordEnconde = new BCryptPasswordEncoder();

    /**
     * 用户登录
     * 1.参数校验(@valid)
     * 2.参数判断
     * mobile---查数据库  有没有这个手机号
     * 有  抛异常
     * smsCode---到redis里面去查  看是不是一样
     * 如果不一样  抛异常
     * 3.因为user需要存loginId  所以先存login表
     * 存好之后需要给我一个loginId
     * 4.保存user相关的表
     * user
     * user-base-info
     * user-account
     * 5.删除redis中对应的手机验证码
     *
     * @param dto
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public JSONResult register(UserRegisterDTO dto) {
        // 判断验证码是否正确
//        AssertUtil.isTrue(getSmsCode(dto),CommonExceptionEnum.SEND_CODE_ERROR.getMessage(),
//                CommonExceptionEnum.SEND_CODE_ERROR.getCode());
        // 判断手机号的验证码是否正确
        String smsCode = dto.getSmsCode();
        // 拼接手机验证码的key
        String smsKey = RedisConstant.REDIS_SMS_CODE_PREFIX + dto.getMobile();
        // 根据从redis中拿取手机验码
        RedisSMSCodeValueDto o = (RedisSMSCodeValueDto)redisTemplate.opsForValue().get(smsKey);
//        RedisSMSCodeValueDto smsCodeTmp =(RedisSMSCodeValueDto) redisTemplate.opsForValue().get(smsKey);
        if (!StringUtils.equalsIgnoreCase(smsCode,o.getCode())){
            throw new GlobalException(CommonExceptionEnum.SEND_CODE_ERROR.getMessage(),
               CommonExceptionEnum.SEND_CODE_ERROR.getCode());
        }

        // 判断手机号是否存在
        AssertUtil.isFalse(existUserMobile(dto.getMobile()),
                CommonExceptionEnum.REGISTER_PHONE.getMessage(),CommonExceptionEnum.REGISTER_PHONE.getCode());
        // 前面都判断之后，就开始将此时的信息保存到login表中
        Login login = new Login();
        // 将密码使用认证时的编码方式
        String encode = passwordEnconde.encode(dto.getPassword());
        login.setPassword(encode);
        login.setUsername(dto.getMobile());
        // 使用负载均衡调用uaa微服务中的login,得到一个login的json格式
        JSONResult jsonResult = uaaFeignClient.saveOrUpdate(login);
        ObjectMapper objectMapper = new ObjectMapper();
        Login loginResult = objectMapper.convertValue(jsonResult.getData(), Login.class);
        Long id = loginResult.getId();

        // 将信心保存到user表中
        User user = new User();
        user.setLoginId(id);
        user.setPhone(dto.getMobile());
        user.setCreateTime(new Date().getTime());
        user.setNickName(dto.getMobile());
        Long batchAddState = BitStatesUtils.batchAddState(0L, BitStatesUtils.OP_ACTIVED,
                BitStatesUtils.OP_REGISTED, BitStatesUtils.OP_AUTHED_PHONE);
        user.setBitState(batchAddState);
        userMapper.insert(user);

        // 生成随机字符串，作为该用户的自己的推荐码：
        UserRedCode userRedCode = new UserRedCode();
        userRedCode.setUserId(user.getId());
        // 生成4位邀请码
        String refCode = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 4).toUpperCase();
        userRedCode.setCode(refCode);
        userRedCodeMapper.insert(userRedCode);

        // 将信息保存到user的垂直分表userinfo中
        UserBaseInfo userBaseInfo = new UserBaseInfo();
        userBaseInfo.setId(user.getId());
        userBaseInfo.setCreateTime(user.getCreateTime());
        userBaseInfo.setRegChannel(dto.getRegChannel());


        // 根据code找到该推荐吗的推荐人id
        UserRedCode userCode = new UserRedCode();
        if (dto.getRefCode() != null) {
            userCode.setCode(dto.getRefCode());
            UserRedCode redCode = userRedCodeMapper.selectOne(userCode);
            if (redCode != null){
                // 将推荐人的id设置为用户表中
                userBaseInfo.setReferId(redCode.getUserId());
            }
        }

        userBaseInfoMapper.insert(userBaseInfo);


        // 将信息保存到useraccount中
        UserAccount userAccount = new UserAccount();
        userAccount.setId(user.getId());
        userAccount.setCreateTime(user.getCreateTime());
        userAccountMapper.insert(userAccount);


        // 最后清除redis中的数据
        redisTemplate.delete(RedisConstant.REDIS_SMS_CODE_PREFIX + dto.getMobile());

        return JSONResult.success();
    }

    /**
    * @Description: 手机验证码检查
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/7 19:21
    */
    public Boolean getSmsCode(UserRegisterDTO dto){
        // 判断手机号的验证码是否正确
        String smsCode = dto.getSmsCode();
        // 拼接手机验证码的key
        String smsKey = RedisConstant.REDIS_SMS_CODE_PREFIX + dto.getMobile();
        // 根据从redis中拿取手机验码
        String smsCodeTmp = (String)redisTemplate.opsForValue().get(smsKey);
        // 判断两个验证码是否相等
        return StringUtils.equals(smsCode,smsCodeTmp);
    }

    /**
    * @Description: 手机号是否注册检验
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/7 19:21
    */
    // 使用jar303判空，然后判断手机号是否已经注册
    public Boolean existUserMobile(String mobile) {
        EntityWrapper<User> queryWrapper = new EntityWrapper<User>();
        queryWrapper.eq("phone", mobile);
        List list = userMapper.selectList(queryWrapper);
        return list != null && list.size() > 0;
    }

    //
}

